package com.vision.sales.sv.serv.rpc;

import java.util.ArrayList;
import java.util.List;

import net.sf.gilead.core.hibernate.HibernateUtil;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.sencha.gxt.data.shared.loader.PagingLoadResult;
import com.sencha.gxt.data.shared.loader.PagingLoadResultBean;
import com.vision.core.cm.data.Domain;
import com.vision.core.cm.db.data.Company;
import com.vision.core.cm.exception.RpcException;
import com.vision.core.sv.db.mgr.IDataManager;
import com.vision.core.sv.db.mgr.ISimpleCriteria;
import com.vision.core.sv.db.mgr.SimpleCriteria;
import com.vision.core.sv.serv.rpc.AbstractServiceImpl;
import com.vision.core.sv.util.CollectionUtils;
import com.vision.sales.cl.ui.component.TransactionLoadConfig;
import com.vision.sales.cm.data.SalesPermissions;
import com.vision.sales.cm.data.TransactionStatus;
import com.vision.sales.cm.db.data.AdvancePayment;
import com.vision.sales.cm.serv.rpc.AdvancePaymentService;
import com.vision.sales.sv.util.TransactionHelper;

/**
 * @author Mark
 *
 */
@Service("advance_payment")
public class AdvancePaymentServiceImpl extends AbstractServiceImpl<AdvancePayment> implements AdvancePaymentService {

	private static final long serialVersionUID = 1L;
	
	private static final Logger log = LoggerFactory.getLogger(AdvancePaymentServiceImpl.class);
	
	public AdvancePaymentServiceImpl() {
		initGilead();
	}
	
	@Autowired
	@Qualifier("advancePaymentManager")
	@Override
	public void setDataManager(IDataManager<AdvancePayment> dataManager) {
		super.setDataManager(dataManager);
	}
	
	@Override
	protected Domain getDomain() {
		return Domain.ADVANCE_PAYMENT;
	}
	
	// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	@Transactional
	@Override
	public AdvancePayment create(AdvancePayment payment) throws RpcException {
		checkPermissions(SalesPermissions.CreateAdvancePayment);
		checkHasCompany(payment.getCompany());
		checkFieldUnique("advancePaymentNo", payment.getAdvancePaymentNo(), "A/P No.");

		// Set applied amount to zero and balance same as the original amount
		payment.setAppliedAmount(0d);
		payment.setBalance(payment.getAmount());
		
		AdvancePayment result = super.create(payment);
		
		logCreate(result);
		
		return result;
	}
	
	@Transactional
	@Override
	public AdvancePayment update(AdvancePayment payment) throws RpcException {
		checkPermissions(SalesPermissions.UpdateAdvancePayment);
		
		AdvancePayment existing = checkExisting(payment.getId());
		
		AdvancePayment result = super.update(payment);
		
		logUpdate(existing, result);
		
		return result;
	}
	
	@Transactional
	@Override
	public AdvancePayment delete(AdvancePayment payment) throws RpcException {
		checkPermissions(SalesPermissions.DeleteAdvancePayment);
		
		AdvancePayment result = super.delete(payment);
		
		logDelete(payment);
		
		return result;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public PagingLoadResult<AdvancePayment> getList(TransactionLoadConfig config) throws RpcException {
		checkPermissions(SalesPermissions.ViewAdvancePayment);
		
		config.setCompanies(new ArrayList<Company>(getLoggedInUser().getCompanies()));

		ISimpleCriteria crit = getCriteria(config);
		
		List<AdvancePayment> list = (List<AdvancePayment>) TransactionHelper.getList(crit, config, getTemplate());
		
		ISimpleCriteria countCrit = TransactionHelper.createCountCriteria(AdvancePayment.class, config);
		Long count = (Long) getTemplate().findByCriteria(countCrit.getDetached()).get(0);
		
		PagingLoadResultBean<AdvancePayment> result = new PagingLoadResultBean<AdvancePayment>(list, count.intValue(), config.getOffset());
		return result;
	}
	
	private ISimpleCriteria getCriteria(TransactionLoadConfig config) {	
		
		ISimpleCriteria crit = SimpleCriteria.forClass(AdvancePayment.class);
	
		// FIXME What if the user is not assigned to any company?
		if (!CollectionUtils.isNullOrEmpty(config.getCompanies())) {
			List<Long> ids = CollectionUtils.listFrom(config.getCompanies(), "getId");
			crit.in("company.id", ids);
		}
		
		if (config.getStart() != null && config.getEnd() != null)
			crit.between("date", config.getStart(), config.getEnd());
		
		if (!CollectionUtils.isNullOrEmpty(config.getProducts())) {
			List<Long> ids = CollectionUtils.listFrom(config.getProducts(), "getId");
			crit.in("product.id", ids);
		}
		
		if (!CollectionUtils.isNullOrEmpty(config.getSalesmen())) {
			List<Long> ids = CollectionUtils.listFrom(config.getSalesmen(), "getId");
			crit.in("salesman.id", ids);
		}
		
		if (!CollectionUtils.isNullOrEmpty(config.getCustomers())) {
			List<Long> ids = CollectionUtils.listFrom(config.getCustomers(), "getId");
			crit.in("customer.id", ids);
		}
		
		crit.desc("date");
		return crit;
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<AdvancePayment> getAdvancePayments(TransactionStatus status) throws RpcException {
		ISimpleCriteria crit = SimpleCriteria.forClass(AdvancePayment.class);
		crit.eq("status", status);
		List<AdvancePayment> list = getTemplate().findByCriteria(crit.getDetached());
		return list;
	}
	
//	@Transactional
//	@Override
//	public List<AdvancePayment> postAdvancePayments(List<AdvancePayment> payments) throws RpcException {
//		checkPermissions(SalesPermissions.PostAdvancePayment);
//		
//		List<Long> ids = CollectionUtils.listFrom(payments, "getId");
//		List<AdvancePayment> existingPayments = getDataManager().getByIds(AdvancePayment.COL_ID, ids);
//		if (existingPayments.isEmpty())
//			return existingPayments;
//		
//		List<AdvancePayment> results = new ArrayList<AdvancePayment>(existingPayments.size());
//		
//		for (AdvancePayment existing : existingPayments) {
//			checkHasCompany(existing.getCompany());
//			
//
//			log.debug("Posting advance payment {}...", existing.getAdvancePaymentNo());
//			existing.setStatus(TransactionStatus.POSTED);
//			AdvancePayment result = getDataManager().update(existing);
//			
//			// NOTE While AdvancePayment has a TransactionStatus field, no TransactionEntry
//			// well be created when an AdvancePayment is posted. A TransactionEntry will
//			// be created only when a posted AdvancePayment is applied to a (posted)
//			// SalesInvoice, from which an AppliedAdvancePayment instance is created.
//			
//			// Log the update
//			logUpdate(existing, result);
//			
//			results.add(result);
//		}
//		
//		return results;
//	}
	
	@Override
	public List<AdvancePayment> getAdvancePaymentsIncludingAppliedPayments(long customerId) throws RpcException {
		ISimpleCriteria paymentsCrit = SimpleCriteria.forClass(AdvancePayment.class);
		paymentsCrit.eq("customer.id", customerId);
		List<AdvancePayment> advancePayments = findByCriteria(paymentsCrit);

		List<AdvancePayment> list = new ArrayList<AdvancePayment>();
		// TODO 0 Find a better way to load the applied payments into the advance payments
		for (AdvancePayment ap : advancePayments) {
			Object z = HibernateUtil.getInstance().loadAssociation(AdvancePayment.class, ap.getId(), "appliedAdvancePayments");
			list.add((AdvancePayment) z);
		}
		
//		ISimpleCriteria appliedPaymentsCrit = SimpleCriteria.forClass(AppliedAdvancePayment.class);
//		appliedPaymentsCrit.eq("customer.id", customerId);
//		List<AppliedAdvancePayment> appliedPayments = findByCriteria(appliedPaymentsCrit);
		
//		for (AppliedAdvancePayment appliedPayment : appliedPayments) {
//			AdvancePayment payment = advancePaymentsMap.get(appliedPayment.getAdvancePayment().getId());
//			Set<AppliedAdvancePayment> list = payment.getAppliedAdvancePayments();
//			if (list == null)
//				payment.setAppliedAdvancePayments(list = new HashSet<AppliedAdvancePayment>());
//			list.add(appliedPayment);
//		}
		
		return list;
	}

}
